// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package ir

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/types"
	"cmd/internal/obj"
	"cmd/internal/src"
	"fmt"
)

// Func对应Go程序中的一个函数
// （反之亦然：每个函数都用一个*Func表示）。
// 
// IR中有多个节点代表一个Func。
// 
// ONAME节点（Func.Nname）用于对其进行简单引用。
// ODCLFUNC节点（Func本身）用于其声明代码。
// OCLOSURE节点（Func.OCLOSURE）用于引用
// 函数文本。
// 
// 导入的函数将有一个ONAME节点，该节点指向一个带有空体的Func 
// 函数。
// 声明的函数或方法有一个ODCLFUNC（函数本身）和一个ONAME。
// 函数文本直接由一个OCLOSURE表示，但它也有一个ODCLFUNC（和一个匹配的ONAME），表示编译后的
// 闭包的基本形式，它使用在寄存器中传递的特殊数据结构访问捕获的变量
// 。
// 
// 方法声明用函数表示，除了f.Sym 
// 将是限定的方法名称（例如，“T.m”）和
// f.Func。Shortname是简单的方法名（例如，“m”）。
// 
// 方法表达式（T.M）表示为OMETHEXPR节点，其中n.Left和n.Right分别指向类型和方法。
// 源代码中对方法表达式的每一次不同提及
// 都会构造一个新节点。
// 
// 方法值（t.M）在直接调用时由ODOTMETH/ODOTINTER 
// 表示，否则由OMETHVALUE表示。
// 这些都是类似的方法表达式，只是对于ODOTMETH/ODOTINTER，
// 方法名称存储在Sym中，而不是右边。
// 每个OMETHVALUE最终被实现为一个新的
// 函数，有点像闭包，有自己的ODCLFUNC。
// OMETHVALUE使用n.Func记录到
// 生成的ODCLFUNC的链接，但没有从Func返回到OMETHVALUE的
// 指针。
type Func struct {
	miniNode
	Body Nodes
	Iota int64

	Nname    *Name        // ONAME节点
	OClosure *ClosureExpr // OCLOSURE节点

	Shortname *types.Sym

	// 函数的额外输入代码。例如，为转义参数分配并初始化
	// 内存。
	Enter Nodes
	Exit  Nodes

	// 此函数/闭包的所有参数/局部变量的ONAME节点，在遍历期间转换闭包之前，不包含closurevars。
	// 名称必须按与函数签名对应的顺序列出PPARAMs、PPARAMOUTs，然后是PAUTOs、
	// 以及PPARAMs和PPARAMOUTs。
	// 然而，由于匿名或空白PPARAMs并未实际声明，因此Dcl中省略了它们。
	// 匿名和空白PPARAMOUTs分别声明为~rNN和~bNN名称。
	Dcl []*Name

	// ClosureVars列出了
	// 函数文本中使用的自由变量，但在封闭的
	// 函数中正式声明。此切片中的变量是闭包函数的
	// 自己的变量副本，在其函数
	// 正文中使用。它们还将分别具有IsClosureVar集，如果它们被值捕获，则将具有
	// Byval集。
	ClosureVars []*Name

	// 需要编译的封闭函数。
	// 步行时居住。
	Closures []*Func

	// Parents在
	// 函数中记录每个作用域的父作用域。根作用域（0）没有父级，因此第i个
	// 作用域的父级存储在父级[i-1]中。
	Parents []ScopeID

	// 标记记录范围边界的更改。
	Marks []Mark

	FieldTrack map[*obj.LSym]struct{}
	DebugInfo  interface{}
	LSym       *obj.LSym // 此函数的本机ABI（Func.ABI）中的链接器对象

	Inl *Inline

	// Closgen跟踪
	// 此函数中生成了多少个闭包。closurename用于创建唯一的
	// 函数名。
	Closgen int32

	Label int32 // 此函数中自动生成的最大标签

	Endlineno src.XPos
	WBPos     src.XPos // 第一个写屏障的位置；参见SetWBPos 

	Pragma PragmaFlag // go:xxx函数注释

	flags bitset16

	// ABI是函数的“定义”ABI。这是
	// 该函数生成的代码期望被调用的ABI。
	// 
	// 对于大多数函数，这将是obj。阿比内特。它可能是
	// 汇编或ABI包装中定义的函数的不同ABI。
	// 
	// 这包括在出口数据中，并在各个包中进行跟踪。
	ABI obj.ABI
	// ABIRefs是引用此函数的ABI集合。
	// 对于除此函数定义ABI之外的ABI，
	// 编译器生成ABI包装函数。这只在一个包中跟踪
	// 。
	ABIRefs obj.ABISet

	NumDefers  int32 // 函数中的延迟调用数
	NumReturns int32 // 函数中的显式返回数

	// nwbrCalls记录此
	// 函数调用的函数的LSyms，用于go:nowritebarrierrec分析。仅填写
	// 如果不需要，请检查！=无
	NWBRCalls *[]SymAndPos

	// 对于包装函数，WrappedFunc指向原始Func。
	// 目前仅用于go/defer包装。
	WrappedFunc *Func
}

func NewFunc(pos src.XPos) *Func {
	f := new(Func)
	f.pos = pos
	f.op = ODCLFUNC
	f.Iota = -1
	// 大多数函数都是内部函数。进口商或symabis 
	// 通行证可能会覆盖此项。
	f.ABI = obj.ABIInternal
	return f
}

func (f *Func) isStmt() {}

func (n *Func) copy() Node                         { panic(n.no("copy")) }
func (n *Func) doChildren(do func(Node) bool) bool { return doNodes(n.Body, do) }
func (n *Func) editChildren(edit func(Node) Node)  { editNodes(n.Body, edit) }

func (f *Func) Type() *types.Type                { return f.Nname.Type() }
func (f *Func) Sym() *types.Sym                  { return f.Nname.Sym() }
func (f *Func) Linksym() *obj.LSym               { return f.Nname.Linksym() }
func (f *Func) LinksymABI(abi obj.ABI) *obj.LSym { return f.Nname.LinksymABI(abi) }

// 内联函数包含用于函数体的字段，这些函数体可以内联。
type Inline struct {
	Cost int32 // 内联此函数的启发式成本

	// 函数的副本。Dcl和Func。内联时使用的阀体。需要复制
	// 因为函数的dcl/体可能会被以后的编译器
	// 转换更改。当从
	// 导入另一个包中的函数时，也会填充这些字段。
	Dcl  []*Name
	Body []Node

	// CanDelayResults报告内联程序延迟
	// 初始化结果参数直到紧接着
	// “return”语句之前是否安全。
	CanDelayResults bool
}

// 标记代表范围边界。
type Mark struct {
	// Pos是标记范围
	// 更改的标记的位置。
	Pos src.XPos

	// Scope标识位置右侧最里面的作用域。
	Scope ScopeID
}

// ScopeID表示函数中的词法作用域。
type ScopeID int32

const (
	funcDupok         = 1 << iota // 重复定义ok 
	funcWrapper                   // 对用户隐藏帧（elide在回溯中，不算作恢复（）的帧）
	funcABIWrapper                // 是ABI包装器（也设置了flagWrapper）
	funcNeedctxt                  // 函数使用上下文寄存器（有闭包变量）
	funcReflectMethod             // 函数调用反映。类型方法或MethodByName 
	// 如果闭包在函数中，则为true；如果简单函数或
	// 全局变量初始化中的闭包
	funcIsHiddenClosure
	funcIsDeadcodeClosure        // 如果闭包是死代码
	funcHasDefer                 // 包含延迟语句
	funcNilCheckDisabled         // 编译此函数时禁用零检查
	funcInlinabilityChecked      // 内联已确定函数是否可内联
	funcExportInline             // 在导出数据
	funcInstrumentBody           // 在SSA构建期间添加race/msan/asan指令
	funcOpenCodedDeferDisallowed // 无法执行开放编码延迟
	funcClosureCalled            // 只立即调用闭包；用于转义分析
)

type SymAndPos struct {
	Sym *obj.LSym // LSym被调用方
	Pos src.XPos  // 调用行
}

func (f *Func) Dupok() bool                    { return f.flags&funcDupok != 0 }
func (f *Func) Wrapper() bool                  { return f.flags&funcWrapper != 0 }
func (f *Func) ABIWrapper() bool               { return f.flags&funcABIWrapper != 0 }
func (f *Func) Needctxt() bool                 { return f.flags&funcNeedctxt != 0 }
func (f *Func) ReflectMethod() bool            { return f.flags&funcReflectMethod != 0 }
func (f *Func) IsHiddenClosure() bool          { return f.flags&funcIsHiddenClosure != 0 }
func (f *Func) IsDeadcodeClosure() bool        { return f.flags&funcIsDeadcodeClosure != 0 }
func (f *Func) HasDefer() bool                 { return f.flags&funcHasDefer != 0 }
func (f *Func) NilCheckDisabled() bool         { return f.flags&funcNilCheckDisabled != 0 }
func (f *Func) InlinabilityChecked() bool      { return f.flags&funcInlinabilityChecked != 0 }
func (f *Func) ExportInline() bool             { return f.flags&funcExportInline != 0 }
func (f *Func) InstrumentBody() bool           { return f.flags&funcInstrumentBody != 0 }
func (f *Func) OpenCodedDeferDisallowed() bool { return f.flags&funcOpenCodedDeferDisallowed != 0 }
func (f *Func) ClosureCalled() bool            { return f.flags&funcClosureCalled != 0 }

func (f *Func) SetDupok(b bool)                    { f.flags.set(funcDupok, b) }
func (f *Func) SetWrapper(b bool)                  { f.flags.set(funcWrapper, b) }
func (f *Func) SetABIWrapper(b bool)               { f.flags.set(funcABIWrapper, b) }
func (f *Func) SetNeedctxt(b bool)                 { f.flags.set(funcNeedctxt, b) }
func (f *Func) SetReflectMethod(b bool)            { f.flags.set(funcReflectMethod, b) }
func (f *Func) SetIsHiddenClosure(b bool)          { f.flags.set(funcIsHiddenClosure, b) }
func (f *Func) SetIsDeadcodeClosure(b bool)        { f.flags.set(funcIsDeadcodeClosure, b) }
func (f *Func) SetHasDefer(b bool)                 { f.flags.set(funcHasDefer, b) }
func (f *Func) SetNilCheckDisabled(b bool)         { f.flags.set(funcNilCheckDisabled, b) }
func (f *Func) SetInlinabilityChecked(b bool)      { f.flags.set(funcInlinabilityChecked, b) }
func (f *Func) SetExportInline(b bool)             { f.flags.set(funcExportInline, b) }
func (f *Func) SetInstrumentBody(b bool)           { f.flags.set(funcInstrumentBody, b) }
func (f *Func) SetOpenCodedDeferDisallowed(b bool) { f.flags.set(funcOpenCodedDeferDisallowed, b) }
func (f *Func) SetClosureCalled(b bool)            { f.flags.set(funcClosureCalled, b) }

func (f *Func) SetWBPos(pos src.XPos) {
	if base.Debug.WB != 0 {
		base.WarnfAt(pos, "write barrier")
	}
	if !f.WBPos.IsKnown() {
		f.WBPos = pos
	}
}

// FuncName返回函数n的名称（不带包）。
func FuncName(f *Func) string {
	if f == nil || f.Nname == nil {
		return "<nil>"
	}
	return f.Sym().Name
}

// PkgFuncName返回由n引用的函数的名称，并带有包前缀。
// 这与编译器的内部约定不同，在编译器内部约定中，本地函数缺少一个包
// 因为最终的使用者是查看IDE的人；如果编译包实际上是空字符串，则包仅为空
// 。
func PkgFuncName(f *Func) string {
	if f == nil || f.Nname == nil {
		return "<nil>"
	}
	s := f.Sym()
	pkg := s.Pkg

	p := base.Ctxt.Pkgpath
	if pkg != nil && pkg.Path != "" {
		p = pkg.Path
	}
	if p == "" {
		return s.Name
	}
	return p + "." + s.Name
}

var CurFunc *Func

// WithFunc调用do with CurFunc和base。Pos设置为curfn和
// curfn。Pos（），然后在返回之前恢复其以前的值
// 。
func WithFunc(curfn *Func, do func()) {
	oldfn, oldpos := CurFunc, base.Pos
	defer func() { CurFunc, base.Pos = oldfn, oldpos }()

	CurFunc, base.Pos = curfn, curfn.Pos()
	do()
}

func FuncSymName(s *types.Sym) string {
	return s.Name + "·f"
}

// MarkFunc将节点标记为函数。
func MarkFunc(n *Name) {
	if n.Op() != ONAME || n.Class != Pxxx {
		base.FatalfAt(n.Pos(), "expected ONAME/Pxxx node, got %v (%v/%v)", n, n.Op(), n.Class)
	}

	n.Class = PFUNC
	n.Sym().SetFunc(true)
}

// ClosureDebugRuntimeCheck对调试标志
// 和编译运行时
func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
	if base.Debug.Closure > 0 {
		if clo.Esc() == EscHeap {
			base.WarnfAt(clo.Pos(), "heap closure, captured vars = %v", clo.Func.ClosureVars)
		} else {
			base.WarnfAt(clo.Pos(), "stack closure, captured vars = %v", clo.Func.ClosureVars)
		}
	}
	if base.Flag.CompilingRuntime && clo.Esc() == EscHeap && !clo.IsGoWrap {
		base.ErrorfAt(clo.Pos(), "heap-allocated closure %s, not allowed in runtime", FuncName(clo.Func))
	}
}

// IstrivalClosure报告Close clo是否有一个
// 捕获变量的空列表。
func IsTrivialClosure(clo *ClosureExpr) bool {
	return len(clo.Func.ClosureVars) == 0
}

// globClosgen就像Func。克罗斯根，但在全球范围内。
var globClosgen int32

// closureName为outerfn中的闭包生成一个新的唯一名称。
func closureName(outerfn *Func) *types.Sym {
	pkg := types.LocalPkg
	outer := "glob."
	prefix := "func"
	gen := &globClosgen

	if outerfn != nil {
		if outerfn.OClosure != nil {
			prefix = ""
		}

		pkg = outerfn.Sym().Pkg
		outer = FuncName(outerfn)

		// 可能有多个名为“z”的函数。在那些
		// 案例中，我们不能使用他们的个人密码，因为这会导致名字冲突。
		if !IsBlank(outerfn.Nname) {
			gen = &outerfn.Closgen
		}
	}

	*gen++
	return pkg.Lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
}

// NewClosureFunc创建一个新Func来表示函数文本。
// 如果hidden为true，则闭包被标记为hidden（即，作为另一个函数中包含的
// 函数文本，而不是
// 包范围变量初始化表达式）。
func NewClosureFunc(pos src.XPos, hidden bool) *Func {
	fn := NewFunc(pos)
	fn.SetIsHiddenClosure(hidden)

	fn.Nname = NewNameAt(pos, BlankNode.Sym())
	fn.Nname.Func = fn
	fn.Nname.Defn = fn

	fn.OClosure = NewClosureExpr(pos, fn)

	return fn
}

// NameClosure为给定的函数literal生成一个唯一的函数，
// 它必须出现在outerfn中。
func NameClosure(clo *ClosureExpr, outerfn *Func) {
	fn := clo.Func
	if fn.IsHiddenClosure() != (outerfn != nil) {
		base.FatalfAt(clo.Pos(), "closure naming inconsistency: hidden %v, but outer %v", fn.IsHiddenClosure(), outerfn)
	}

	name := fn.Nname
	if !IsBlank(name) {
		base.FatalfAt(clo.Pos(), "closure already named: %v", name)
	}

	name.SetSym(closureName(outerfn))
	MarkFunc(name)
}

// UseClosure检查是否已设置了ginen函数文本
// 正确，然后将其作为表达式返回。
// 必须在clo之后调用。Func。ClosureVars已经设置好了。注意：clo。
func UseClosure(clo *ClosureExpr, pkg *Package) Node {
	fn := clo.Func
	name := fn.Nname

	if IsBlank(name) {
		base.FatalfAt(fn.Pos(), "unnamed closure func: %v", fn)
	}
	// tcClosure调用UseClosure时，Typecheck（）仍为0。
	if fn.Typecheck() != 1 || name.Typecheck() != 1 {
		base.FatalfAt(fn.Pos(), "missed typecheck: %v", fn)
	}
	if clo.Type() == nil || name.Type() == nil {
		base.FatalfAt(fn.Pos(), "missing types: %v", fn)
	}
	if !types.Identical(clo.Type(), name.Type()) {
		base.FatalfAt(fn.Pos(), "mismatched types: %v", fn)
	}

	if base.Flag.W > 1 {
		s := fmt.Sprintf("new closure func: %v", fn)
		Dump(s, fn)
	}

	if pkg != nil {
		pkg.Decls = append(pkg.Decls, fn)
	}

	if false && IsTrivialClosure(clo) {
		// TODO（mdempsky）：调查我们是否可以/应该优化这个
		// 案例。walkClosure稍后已经处理了它，但早期识别它可能很有用（例如，它可能允许多个对函数的内联调用共享一个普通的闭包，而不是为每个内联调用克隆它）。
	}

	return clo
}
